package com.dianwoba.encrypt;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.dianwoba.core.exception.BusinessException;

public final class IdEncryptUtil {
	
	private static final String AES_SESSION_KEY = "_DIANWOBASEA_";
	private static final String AES_SESSION_IV = "_DIANWOBAIV_";
	private static final String ACCESS_FORM_EXTERNAL = "EXTERNAL";

	private static final String appFixedAesKey = DigestUtils.md5Hex(AES_SESSION_KEY + AES_SESSION_IV);

	public static String getAppFixedAesKey() {
		return appFixedAesKey;
	}
	
	/**
	 * 根据Session加密
	 * @param id
	 * @return
	 */
	public static String aesEncode(int id) {
		return aesEncode(Long.valueOf(id));
	}

	/**
	 * 根据Session加密
	 * @param id
	 * @return
	 */
	public static String aesEncode(long id) {
		byte[] input = String.valueOf(id).getBytes();
		Map<String, byte[]> keys = getAesKeyIV();
		byte[] key = keys.get(AES_SESSION_KEY);
		byte[] iv = keys.get(AES_SESSION_IV);

		return Hex.encodeHexString(Cryptos.aesEncrypt(input, key, iv));
	}
	
	/**
	 * 根据Session解密
	 * @param input
	 * @return
	 */
	public static Long aesDecode(String input) {
		return aesDecode(input, null);
	}
	
	/**
	 * 固定加密
	 * @param id
	 * @return
	 */
	public static String aesFixEncode(long id) {
		byte[] input = String.valueOf(id).getBytes();
		byte[] key = decodeHex(getAppFixedAesKey());
		byte[] encryptResult = Cryptos.aesEncrypt(input, key);

		return Hex.encodeHexString(encryptResult);
	}

	/**
	 * 固定解密
	 * @param input
	 * @return
	 */
	public static Long aesFixDecode(String input) {
		return aesDecode(input, ACCESS_FORM_EXTERNAL);
	}
	
	private static Long aesDecode(String input, String accessFrom) {
		try {
			String value = "-110";
			if (ACCESS_FORM_EXTERNAL.equalsIgnoreCase(accessFrom)) {
				value = aesDecodeExternal(input);
			} else {
				Map<String, byte[]> keys = getAesKeyIV();
				byte[] key = keys.get(AES_SESSION_KEY);
				byte[] iv = keys.get(AES_SESSION_IV);
				
				value = Cryptos.aesDecrypt(decodeHex(input), key, iv);
			}
			return Long.parseLong(value);
		} catch (Exception e) {
			throw new BusinessException("请求已失效，请重新登录系统重试！", e);
		}
	}

	private static Map<String, byte[]> getAesKeyIV() {
		return getAesKeyIVFromSession();
	}

	private static Map<String, byte[]> getAesKeyIVFromSession() {
		Map<String, byte[]> keys = new HashMap<String, byte[]>();
		
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes()).getRequest();
		HttpSession session = request.getSession();
		
		if (session.getAttribute(AES_SESSION_KEY) == null) {
			session.setAttribute(AES_SESSION_KEY, Cryptos.generateAesKey());
		}

		if (session.getAttribute(AES_SESSION_IV) == null) {
			session.setAttribute(AES_SESSION_IV, Cryptos.generateIV());
		}
		keys.put(AES_SESSION_KEY, (byte[]) session.getAttribute(AES_SESSION_KEY));
		keys.put(AES_SESSION_IV, (byte[]) session.getAttribute(AES_SESSION_IV));

		return keys;
	}

	private static String aesDecodeExternal(String input) {
		return aesDecodeExternal(input, getAppFixedAesKey());
	}

	private static String aesDecodeExternal(String input, String aesKey) {
		String value = "-110";
		byte[] key = decodeHex(aesKey);
		String descryptResult = Cryptos.aesDecrypt(decodeHex(input), key);
		if (StringUtils.isNotBlank(descryptResult)) {
			String[] values = descryptResult.split(":");
			if (values != null) {
				if (values.length == 2) {
					// 过期时间必须依赖external，和server时间基本要保持一致，external调用时设置的过期时间可以长一点如now+4hour
					long time = Long.parseLong(values[1]);
					Date now = new Date();
					if (time > now.getTime()) {
						value = values[0];
					}
				} else if (values.length == 1) {
					value = values[0];
				} else {
					value = descryptResult;
				}
			}
		}

		return value;
	}
	
	/**
     * Hex解码.
     */
    private static byte[] decodeHex(String input) {
        try {
            return Hex.decodeHex(input.toCharArray());
        } catch (DecoderException e) {
            throw new RuntimeException();
        }
    }

	public static void main(String[] args) throws EncoderException {
		for (int i = 1; i < 100; i++) {
			long l = i;
			System.err.println(aesFixEncode(l) + "\t" + aesFixDecode(aesFixEncode(l)) + "\t"
					+ (aesFixDecode(aesFixEncode(l)) == l));
		}
	}
}
